﻿using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Text.RegularExpressions;
using System.Xml.Linq;

namespace MicrosoftSnackDownloader
{
    class Program
    {
        private const string LOG = "C:\\MS Snacks\\download-snacks.log";

        private const string SAVE_DIR = "C:\\MS Snacks\\";

        private const string URL = "http://www.microsoft.com/learning/en/us/training/format-learning-snacks.aspx";

        private const string LINK_REGEX = "href\\s*=\\s*(?:\"(?<1>[^\"]*)\"|(?<1>\\S+))";

        private const string LINK_STARTS_WITH = "http://www.microsoft.com/learning/_silverlight/learningsnacks/";

        private const string CLEAN_FILENAME_REGEX = "[^a-zA-Z0-9_-]";

        private const string PLAYER_CONFIG_NAME = "readinessPlayer.xml";

        private const string ALT_PLAYER_CONFIG_NAME = "contentXml/readinessPlayer.xml";

        private const string MEDIA_EXT = ".wmv";

        static void Log(string message)
        {
            Console.WriteLine(message);
            File.AppendAllText(LOG, message);
        }

        static void Log(string messageFormat, params object[] args)
        {
            var message = string.Format(messageFormat, args);

            Log(message);
        }

        static void Main(string[] args)
        {
            try
            {
                if (!Directory.Exists(SAVE_DIR)) Directory.CreateDirectory(SAVE_DIR);

                if (File.Exists(LOG)) File.Delete(LOG);

                using (var log = File.CreateText(LOG)) { }

                Console.WriteLine("Connecting...");
                var wc = new WebClient();
                var html = wc.DownloadString(URL);
                Console.WriteLine("Parsing links...");
                var regex = new Regex(LINK_REGEX, RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Multiline);
                var fileNameRegex = new Regex(CLEAN_FILENAME_REGEX, RegexOptions.IgnoreCase | RegexOptions.Compiled);
                var links = regex.Matches(html);

                Console.WriteLine("{0} links found!", links.Count);

                foreach (Match link in links)
                {
                    var href = link.Groups["1"].Value.ToLowerInvariant();

                    if (href.Contains(LINK_STARTS_WITH))
                    {
                        href = href.Substring(0, href.LastIndexOf('/') + 1);

                        Console.WriteLine("Processing {0}", href);

                        var playerConfigURL = string.Concat(href, PLAYER_CONFIG_NAME);

                        try
                        {
                            XDocument playerConfigXML = null;

                            try
                            {
                                playerConfigXML = XDocument.Load(playerConfigURL);
                            }
                            catch (WebException xdocex)
                            {
                                Console.WriteLine("ERROR Loading Configuration {0}. Retrying w/ alternate config.", playerConfigURL);

                                playerConfigURL = string.Concat(href, ALT_PLAYER_CONFIG_NAME);
                                playerConfigXML = XDocument.Load(playerConfigURL);
                            }

                            var title = playerConfigXML.Root.Attribute("title");
                            var topics = playerConfigXML.Root.Elements("topic");

                            if (title == null || topics == null) { Log("{0} had no title or topics", href); continue; }
                            if (string.IsNullOrEmpty(title.Value)) { Log("{0} had null or empty title", href); continue; }

                            var dirName = fileNameRegex.Replace(title.Value.Replace(" ", "_"), string.Empty);
                            var dirPath = string.Concat(SAVE_DIR, dirName);

                            // create new dir if not exists
                            if (!Directory.Exists(dirPath)) Directory.CreateDirectory(dirPath);

                            var topicNumber = 1;

                            foreach (var topic in topics)
                            {
                                var name = topic.Attribute("title");
                                var media = topic.Element("media");

                                if (name == null || media == null) { Log("{0} topic #{1} had no title or media", title, topicNumber); continue; }
                                if (string.IsNullOrEmpty(name.Value)) { Log("{0} topic #{1} had null or empty title", title, topicNumber); continue; }
                                if (name.Value.ToLowerInvariant() == "intro") continue;
                                if (string.IsNullOrEmpty(media.Value)) { Log("{0} topic #{1} had null or empty media", title, topicNumber); continue; }

                                if (media.Value.EndsWith(MEDIA_EXT))
                                {
                                    var fileName = string.Concat(fileNameRegex.Replace(name.Value.Replace(" ", "_"), string.Empty), MEDIA_EXT);
                                    // var filePath = string.Concat(dirName, "\\", topicNumber < 10 ? "0" : string.Empty, topicNumber.ToString(), "-", fileName);
                                    var filePath = string.Concat(SAVE_DIR, dirName, "\\", fileName);

                                    if (File.Exists(filePath)) continue;

                                    try
                                    {
                                        Console.WriteLine("Downloading {0} to {1}", media.Value, filePath);

                                        wc.DownloadFile(media.Value, filePath);
                                    }
                                    catch (Exception fileException)
                                    {
                                        Log("ERROR Downloading {0} to {1}", media.Value, filePath);
                                        Log(fileException.Message);
                                    }
                                }

                                topicNumber++;
                            }
                        }
                        catch (WebException webex)
                        {
                            Log("Error getting media for {0}:", playerConfigURL);
                            Log(webex.Message);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Log("CATASTROPHIC FAILURE");
                Log(ex.Message);
                Debug.Fail(ex.Message);
            }

            Console.WriteLine("Done!");
            Console.Read();
        }
    }
}
